#include "EEG_device.h"
#include "ColorfulLog.h"

#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <QMap>
#include <poll.h>
#include <errno.h>
#define DEBUG

getDataStr_TypeDef* EEG_device::get_ClientData(){
    return &ClientData;
}

/*懒汉*/
//初始化静态成员变量
EEG_device *EEG_device::m_SingleInstance = nullptr;
std::mutex EEG_device::m_Mutex;
// 采集循环标志位,初始化时不采集
// bool EEG_device::is_collecting = false;
/**********************************
 * 函数名：GetInstance
 * 功能：获取配置对象单一实例
 * 输入：无
 * 输出：无
 * 备注：无
**********************************/
EEG_device* EEG_device::getInstance(QObject *parent)
{
    //  双检锁，只有判断指针为空的时候才加锁，避免每次调用 GetInstance的方法都加锁
    if (m_SingleInstance == nullptr) 
    {
        std::unique_lock<std::mutex> lock(m_Mutex); // 加锁,自动释放
        if (m_SingleInstance == nullptr)
        {
            volatile auto temp = new (std::nothrow) EEG_device(parent);
            m_SingleInstance = temp;
        }

    }
    return m_SingleInstance;
}

void EEG_device::deleteInstance()
{
    std::unique_lock<std::mutex> lock(m_Mutex); // 加锁
    if (m_SingleInstance)
    {
        delete m_SingleInstance;
        m_SingleInstance = nullptr;
    }
    printf("EEG_device deleted\n");
}

EEG_device::EEG_device(QObject *parent, const char* serverIP, int serverPort):CollectBase(parent)
{
    // 变量初始化
    // bzero(buffer,sizeof(buffer));

    // 结构体初始化
    bzero(ClientData.buffer,sizeof(ClientData.buffer));
    // 读到的数据不写入文件
    ClientData.writeInFile = 0;
    // 初始化时不采集
    // is_connected = false;

    // 创建一个TCP客户端
    ClientData.clientSockFd = socket(AF_INET, SOCK_STREAM, 0);
    // // 非阻塞连接
    // int flags = fcntl(ClientData.clientSockFd, F_GETFL, 0);
    // flags |= O_NONBLOCK;
    // fcntl(ClientData.clientSockFd, F_SETFL, flags);
    

    if(ClientData.clientSockFd == -1){
        printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
    }

    memset(&serverSock, 0, sizeof(serverSock));
    serverSock.sin_family = AF_INET;
    serverSock.sin_port = htons(serverPort);
    serverSock.sin_addr.s_addr = inet_addr(serverIP);

    // // ::connect调用全局connect，不然会调用QWidget的连接函数
    // int res = ::connect(ClientData.clientSockFd, (struct sockaddr*)&serverSock, sizeof(serverSock));

    // // printf("res = %d \n",res);
    // if( res < 0){
    //     printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
    //     changeConnectingState(false);
    // }
    // else{
    //     // is_connected = true;
    //     changeConnectingState(true);
    //     const char *server_ip = inet_ntoa(serverSock.sin_addr);
    //     uint16_t server_port = ntohs(serverSock.sin_port);
    //     printf_green("服务端:[%s:%d]已连接....\n",server_ip,server_port);
    // }
}

EEG_device::~EEG_device()
{
    deleteInstance();
    printf("EEG_device shut down!\n");
}


void EEG_device::run(){
    // printf("eeg collect is running...\n");
    dataCollecting();
}

int EEG_device::dataCollecting(void* pContext){











    // 判断初始化时有无连接，没有则申请连接服务器
    while(!is_connecting()){
        int res = ::connect(ClientData.clientSockFd, (struct sockaddr*)&serverSock, sizeof(serverSock));
        // int res = -1;
        if( res < 0){
            static int counts = 0;
            ++counts;
            printf("connect error: %s(errno: %d)..........retry:%d\n",strerror(errno),errno,counts);
            QEventLoop loop;//定义一个新的事件循环
            QTimer::singleShot(800, &loop, SLOT(quit()));//创建单次定时器，槽函数为事件循环的退出函数
            loop.exec();//事件循环开始执行，程序会卡在这里，直到定时时间到，本循环被退出

            // 重连8次,超时结束脑电采集线程
            if(counts >= 8){
                CollectBase::collectingEEG(false);
                printf_red("=== EEG_device connecting timeout... ===\n");
                counts = 0;
                return -1;
            }
        }
        else{
            // is_connected = true;
            changeConnectingState(true);
            const char *server_ip = inet_ntoa(serverSock.sin_addr);
            uint16_t server_port = ntohs(serverSock.sin_port);
            printf_green("服务端:[%s:%d]已连接....\n",server_ip,server_port);
        }
        
    }

    // getDataStr_TypeDef* pClientData = (getDataStr_TypeDef *)pContext;
    ssize_t fd = -1;
    if(ClientData.writeInFile){
        // 打开一个文件
        fd = open("dataFromServer.txt", O_RDWR | O_CREAT, S_IRUSR|S_IWUSR);
        if(fd == -1){
            printf("file open error: %s(errno: %d)\n",strerror(errno),errno);
        }
    }
    
    int cnt = 0,errrocnt = 0;
    int firstPkg_index = -1;

    // // 阻塞读
    // int flags = fcntl(ClientData.clientSockFd, F_GETFL, 0);
    // // flags |= O_NONBLOCK;
    // flags &= ~O_NONBLOCK;
    // fcntl(ClientData.clientSockFd, F_SETFL, flags);

    // 采集线程主循环
    printf_yellow("EEG_device::dataCollecting...\n");
    struct timeval timeout;
    timeout.tv_sec = 2;  // 超时时间为2秒
    timeout.tv_usec = 0;

    setsockopt(ClientData.clientSockFd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
    while (is_collecting())
    {
        // 从TCP协议中读取数据并解析
        ssize_t readLen = read(ClientData.clientSockFd,ClientData.buffer,sizeof(ClientData.buffer)); //no block

        if (readLen == -1 && errno == EWOULDBLOCK) {
            // 自动重连
            if(0){
                QEventLoop loop;//定义一个新的事件循环
                QTimer::singleShot(50, &loop, SLOT(quit()));//创建单次定时器，槽函数为事件循环的退出函数
                loop.exec();//事件循环开始执行，程序会卡在这里，直到定时时间到，本循环被退出
                readLen = read(ClientData.clientSockFd,ClientData.buffer,sizeof(ClientData.buffer));
                if(readLen == -1){
                    printf("connect again, Server connection error!\n");
                    break;
                }
            }
            else{
                printf("Server connection error!\n");
                break;
            }
        }
        else if (readLen == 0) {
            printf("Server shutdown.\n");
            break;
        } 
        else if (readLen < 0) {
            printf("=== eeg read error!!! === \n");
            break;
            // close(ClientData.clientSockFd);
        } 

        // if(readLen <= 0){
        //     static int counts = 0;
        //     printf("=== eeg read error!!! ===..........%d\n",counts);
        //     // usleep(1000);
        //     sleep(1);
        //     ++counts;
        //     // 超时结束脑电采集线程
        //     if(counts > 5){
        //         printf_red("=== EEG_device connecting timeout... ===\n");
        //         counts = 0;
        //         break;
        //     }
        //     continue;
        // }

        // 读到长度大于0的数据
        if(ClientData.writeInFile){
            int writeLen = write(fd, ClientData.buffer, readLen);
            if(writeLen == -1){
                printf("write error\n");
                sleep(1);
                continue;
            }
        }
        for( int i = 0; i <= readLen - sizeof(TCPEEG_PACK); ++i){
            if( TCPEEG_HEAD_1 == ClientData.buffer[i] && TCPEEG_HEAD_2 == ClientData.buffer[i + 1]){
                firstPkg_index = i;
                break;
            }
        }

        // 没有接收到有效数据
        if( firstPkg_index < 0 ){
            printf(" Frame header verification failed\n");
            return -1;
        }

        // 计算一次接收的数据有多少个数据帧
        int pack_cnt = (readLen - firstPkg_index) / sizeof(TCPEEG_PACK);

        TCPEEG_PACK* buf = new TCPEEG_PACK[pack_cnt];
        if(nullptr == buf){
            printf("内存空间不足\n");
            return -1;
        }
        
        // 所有数据进行拷贝
        memcpy(buf, &ClientData.buffer[firstPkg_index], sizeof(TCPEEG_PACK) * pack_cnt);

        #ifdef DEBUG
        // printf("pack_cnt = %d \n",pack_cnt);
        #endif

        TCPEEG_PACK one_pack;
        EEG_VOLTAGE one_frame;
        
        int temp;
        ClientData.m_aryVoltages.clear();
        for( int i = 0; i < pack_cnt; ++i ){
            memcpy(&one_pack, &buf[i], sizeof(TCPEEG_PACK));
            for( int j = 0; j < TCPEEG_CHANNEL_COUNT; ++j ){
                // temp = 0;
                temp = one_pack.data[j].high;
                temp ^= 0x80;
                temp <<= 8;
                temp |= one_pack.data[j].middle;
                temp <<= 8;
                temp |= one_pack.data[j].low;
                temp -= 8388608;

                // one_frame.data[j] = temp / 8388608.0 * 2500000 / 24.0;
                one_frame.data[j] = temp *4.5*1000*1000/(8388608)/24;

                #undef DEBUG
                #ifdef DEBUG
                #if 0
                printf("val: ch[%02d] = %lf\n",j,one_frame.data[j]);
                usleep(50000);
                #else
                if(j == 25){
                    // printf("val: ch[%02d] = %lf\n",j,one_frame.data[j]);
                    printf("one_frame.data[%d]= %lf\n",j,one_frame.data[j]);
                }
                // usleep(5000);
                #endif
                #endif
            }
            ClientData.m_aryVoltages.push_back(one_frame);
        }
        
        // 信号槽机制将数据发送出去,拷贝一份，不共享内存
        sendDataInsignals = ClientData.m_aryVoltages;
        QVariant sendData;
        sendData.setValue(sendDataInsignals);
        emit send_ClientData(sendData);
        // emit send_ClientData(sendDataInsignals);
        // printf("emit\n");
    }

    printf("eeg_device stop collecting.\n");

    return 0;  
}

const QMap<int,QString>& EEG_device::getElectrodeMap(){
    static QMap<int, QString> electrodeMap;
    if(!electrodeMap.isEmpty())
        return electrodeMap;

    using namespace EEGDEV;
    electrodeMap = QMap<int, QString>({
        {FP1,"FP1"},
        {FPZ,"FPZ"},
        {FP2,"FP2"},
        {AF3,"AF3"},
        {AF4,"AF4"},
        {F7,"F7"},
        {F5,"F5"},
        {F3,"F3"},
        {F1,"F1"},
        {FZ,"FZ"},
        {F2,"F2"},
        {F4,"F4"},
        {F6,"F6"},
        {F8,"F8"},
        {FT7,"FT7"},
        {FC5,"FC5"},
        {FC3,"FC3"},
        {FC1,"FC1"},
        {FCZ,"FCZ"},
        {FC2,"FC2"},
        {FC4,"FC4"},
        {FC6,"FC6"},
        {FT8,"FT8"},
        {T7,"T7"},
        {C5,"C5"},
        {C3,"C3"},
        {C1,"C1"},
        {CZ,"CZ"},
        {C2,"C2"},
        {C4,"C4"},
        {C6,"C6"},
        {T8,"T8"},
        {M1,"M1"},
        {TP7,"TP7"},
        {CP5,"CP5"},
        {CP3,"CP3"},
        {CP1,"CP1"},
        {CPZ,"CPZ"},
        {CP2,"CP2"},
        {CP4,"CP4"},
        {CP6,"CP6"},
        {TP8,"TP8"},
        {M2,"M2"},
        {P7,"P7"},
        {P5,"P5"},
        {P3,"P3"},
        {P1,"P1"},
        {PZ,"PZ"},
        {P2,"P2"},
        {P4,"P4"},
        {P6,"P6"},
        {P8,"P8"},
        {PO7,"PO7"},
        {PO5,"PO5"},
        {PO3,"PO3"},
        {POZ,"POZ"},
        {PO4,"PO4"},
        {PO6,"PO6"},
        {PO8,"PO8"},
        {CB1,"CB1"},
        {O1,"O1"},
        {OZ,"OZ"},
        {O2,"O2"},
        {CB2,"CB2"}
    });


   
    return electrodeMap;
}





// void EEG_device::collectingEEG(bool state){
//     CollectBase::collectingEEG(state);
// }

// bool EEG_device::is_collecting(){
//     return CollectBase::is_collecting();
// }

// void EEG_device::stopCollecting(){
//     CollectBase::collectingEEG(false);

//     this->quit();  //这个会等到数据处理完，才退出
//     this->wait();  //等待结束（会出现阻塞，但是感觉不到），回收资源
// }